package com.sui.bigdata.rtcadmin.exception;

import com.sui.bigdata.rtcadmin.model.vo.CommonResponse;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author YongChen
 * @date 2019/11/6 14:41
 * @description
 * @email yong_chen@sui.com
 */
@RestControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    // 400

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());

        final List<ApiError.ErrorItem> errors = ex.getBindingResult().getFieldErrors()
                .stream()
                .map(e -> ApiError.makeErrorItem(e.getField(), e.getDefaultMessage()))
                .collect(Collectors.toList());

        for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
            errors.add(ApiError.makeErrorItem(error.getObjectName(), error.getDefaultMessage()));
        }
        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, "Method argument not valid exception", errors);
        return new ResponseEntity<>(apiError, headers, status);
    }

    @Override
    protected ResponseEntity<Object> handleBindException(final BindException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final List<ApiError.ErrorItem> errors = ex.getBindingResult().getFieldErrors()
                .stream()
                .map(e -> ApiError.makeErrorItem(e.getField(), e.getDefaultMessage()))
                .collect(Collectors.toList());

        for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
            errors.add(ApiError.makeErrorItem(error.getObjectName(), error.getDefaultMessage()));
        }
        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, "Bind exception", errors);
        return new ResponseEntity<>(apiError, headers, apiError.getStatus());
    }

    @Override
    protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType();

        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST,error, ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @Override
    protected ResponseEntity<Object> handleMissingServletRequestPart(final MissingServletRequestPartException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final String error = ex.getRequestPartName() + " part is missing";
        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, error, ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @Override
    protected ResponseEntity<Object> handleMissingServletRequestParameter(final MissingServletRequestParameterException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final String error = ex.getParameterName() + " parameter is missing";
        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, error, ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    //

    @ExceptionHandler({ MethodArgumentTypeMismatchException.class })
    public ResponseEntity<Object> handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName();

        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, error,  ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @ExceptionHandler({ ConstraintViolationException.class })
    public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException ex, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final List<ApiError.ErrorItem> errors = new ArrayList<ApiError.ErrorItem>();
        for (final ConstraintViolation<?> violation : ex.getConstraintViolations()) {
            errors.add(ApiError.makeErrorItem(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() , violation.getMessage()));
        }

        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    // 404

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL();

        final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, error, ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    // 405

    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final StringBuilder builder = new StringBuilder();
        builder.append(ex.getMethod());
        builder.append(" method is not supported for this request. Supported methods are ");
        ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " "));

        final ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, builder.toString(), ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    // 415

    @Override
    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
        logger.info(ex.getClass().getName());
        //
        final StringBuilder builder = new StringBuilder();
        builder.append(ex.getContentType());
        builder.append(" media type is not supported. Supported media types are ");
        ex.getSupportedMediaTypes().forEach(t -> builder.append(t + " "));

        final ApiError apiError = new ApiError(HttpStatus.UNSUPPORTED_MEDIA_TYPE, builder.substring(0, builder.length() - 2), ex.getLocalizedMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    // 500

//    @ExceptionHandler({ Exception.class })
//    public ResponseEntity<Object> handleAll(final Exception ex, final WebRequest request) {
//        logger.info(ex.getClass().getName());
//        logger.error("error", ex);
//        //
//        final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, "error occurred", ex.getLocalizedMessage());
//        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
//    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> employeeNotFoundHandler(final Exception ex, final WebRequest request) {
        logger.error(ex);
        final ApiError apiError = new ApiError(HttpStatus.OK, ex.getMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @ExceptionHandler(DemoNotFoundException.class)
    public ResponseEntity<Object> employeeNotFoundHandler(final DemoNotFoundException ex, final WebRequest request) {
        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @ExceptionHandler(TokenInvalidException.class)
    public  ResponseEntity<Object> tokenInvalidHandler(TokenInvalidException ex) {
        logger.error("tokenInvalidException：{}", ex);
        final ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, ex.getMessage());
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @ExceptionHandler(TableNameRepeteException.class)
    public CommonResponse tableNameRepeteHandler(TableNameRepeteException e) {
        logger.error("tableNameRepeteException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler(ExecSqlBlankException.class)
    public CommonResponse execSqlBlankHandler(ExecSqlBlankException e) {
        logger.error("execSqlBlankException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler(JobNameBlankException.class)
    public CommonResponse jobNameBlankHandler(JobNameBlankException e) {
        logger.error("jobNameBlankException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse reSubmitJobHandler(ReSubmitJobException e) {
        logger.error("reSubmitJobException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse jobNotRunningHandler(JobNotRunningException e) {
        logger.error("jobNotRunningException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse createSqlBlankHandler(CreateSqlBlankException e) {
        logger.error("createSqlBlankException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse actionNotSupportHandler(ActionNotSupportException e) {
        logger.error("actionNotSupportException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse runtimeExceptionHandler(RuntimeException e) {
        logger.error("runtimeException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }


    @ExceptionHandler(TableNotFoundException.class)
    public CommonResponse tableNotFoundHandler(TableNotFoundException e) {
        logger.error("tableNotFoundException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse jobNotFoundHandler(JobNotFoundException e) {
        logger.error("jobNotFoundException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse developerBlankHandler(DeveloperBlankException e) {
        logger.error("developerBlankException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse jobRestartFailHandler(JobRestartFailException e) {
        logger.error("jobRestartFailException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse jobNotExistHandler(JobNotExistException e) {
        logger.error("jobNotExistException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse reOperationHandler(ReOperationException e) {
        logger.error("reOperationException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse flinkCommonHandler(FlinkCommonException e) {
        logger.error("flinkCommonException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }



    @ExceptionHandler
    public CommonResponse LoginFailHandler(LoginFailException e) {
        logger.error("loginFailException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse sparkApplicationExcetion(SparkApplicationException e){
        logger.error("SparkApplicationException：{}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }

    @ExceptionHandler
    public CommonResponse sparkConfException(SparkConfException e){
        logger.error("SparkConfException: {}", e);
        return CommonResponse.Builder.buildError(e.getMessage());
    }
}
